home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
mach
/
sun4.md
/
machCode.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-06
|
72KB
|
2,356 lines
/*
* machCode.c --
*
* C code for the mach module.
*
* Copyright (C) 1985 Regents of the University of California
* All rights reserved.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/mach/sun4.md/machCode.c,v 9.42 93/01/06 20:09:15 mgbaker Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include <stddef.h>
#include <sprite.h>
#include <swapBuffer.h>
#include <machConst.h>
#include <machMon.h>
#include <machInt.h>
#include <mach.h>
#include <proc.h>
#include <prof.h>
#include <sys.h>
#include <sched.h>
#include <vm.h>
#include <vmMach.h>
#include <user/sun4.md/sys/machSignal.h>
#include <procUnixStubs.h>
#include <stdlib.h>
#include <stdio.h>
#include <bstring.h>
#include <compatInt.h>
#include <ctype.h>
#include <recov.h>
/*
* Number of processors in the system.
*/
#ifndef NUM_PROCESSORS
#define NUM_PROCESSORS 1
#endif NUM_PROCESSORS
int mach_NumProcessors = NUM_PROCESSORS;
/*
* The following two variables should be in the initialized data space, but
* marked as not initialized. Then, when they are updated as part of booting,
* their new values get preserved over a fast restart.
*/
int storedDataSize = -1;
char *mach_RestartTablePtr = (char *) NIL;
/*
* TRUE if cpu was in kernel mode before the interrupt, FALSE if was in
* user mode.
*/
Boolean mach_KernelMode;
/*
* Sp saved into this before debugger call.
*/
int machSavedRegisterState = 0;
/*
* Flag used by routines to determine if they are running at
* interrupt level.
*/
Boolean mach_AtInterruptLevel = FALSE;
/*
* The machine type string is imported by the file system and
* used when expanding $MACHINE in file names.
*/
char *mach_MachineType = "sun4";
/*
* The byte ordering/alignment type used with Fmt_Convert and I/O control data.
* For compatablity we set this to the old Swap_Buffer constant.
*/
Fmt_Format mach_Format = FMT_SPARC_FORMAT;
/*
* Count of number of ``calls'' to enable interrupts minus number of calls
* to disable interrupts. Kept on a per-processor basis.
*/
int mach_NumDisableInterrupts[NUM_PROCESSORS];
int *mach_NumDisableIntrsPtr = mach_NumDisableInterrupts;
extern int debugProcStubs;
/*
* Machine dependent variables.
*/
Address mach_KernStart;
Address mach_CodeStart;
Address mach_StackBottom;
int mach_KernStackSize;
Address mach_KernEnd;
Address mach_FirstUserAddr;
Address mach_LastUserAddr;
Address mach_MaxUserStackAddr;
int mach_LastUserStackPage;
Address machTBRAddr; /* address of trap table. The value
* is set up and stored here in
* bootSysAsm.s.*/
#define MACH_NUM_VECTORS 256 /* Number of interrupt vector slots */
Address machVectorTable[MACH_NUM_VECTORS]; /* Table of autovector and
* vectored interrupt handlers. */
ClientData machInterruptArgs[MACH_NUM_VECTORS]; /* Table of clientData
* args to pass interrupt handlers */
int machMaxSysCall; /* Hightest defined system call. */
int machArgOffsets[SYS_NUM_SYSCALLS];/* For each system call, tells how
* much to add to the fp at the time
* of the call to get to the highest
* argument on the stack. */
Address machArgDispatch[SYS_NUM_SYSCALLS];/* For each system call, gives an
* address to branch to, in the
* middle of MachFetchArgs, to copy the
* right # of args from user space to
* the kernel's stack. */
ReturnStatus (*(mach_NormalHandlers[SYS_NUM_SYSCALLS]))();
/* For each system call, gives the
* address of the routine to handle
* the call for non-migrated processes.
*/
ReturnStatus (*(mach_MigratedHandlers[SYS_NUM_SYSCALLS]))();
/* For each system call, gives the
* address of the routine to handle
* the call for migrated processes. */
int machKcallTableOffset; /* Byte offset of the kcallTable field
* in a Proc_ControlBlock. */
int machStatePtrOffset; /* Byte offset of the machStatePtr
* field in a Proc_ControlBlock. */
int machSpecialHandlingOffset; /* Byte offset of the specialHandling
* field in a Proc_ControlBlock. */
int machTmpRegsOffset; /* Offset of overflow temp regs. */
int machTmpRegsStore[2]; /* Temporary storage. */
int machGenFlagsOffset; /* offset of genFlags field in a
* Proc_ControlBlock. */
int machForeignFlag; /* Value of PROC_FOREIGN available
* in assembler. */
int MachPIDOffset; /* Byte offset of pid in PCB */
char mach_DebugStack[0x2000]; /* The debugger stack. */
unsigned int machDebugStackStart; /* Contains address of base of debugger
* stack. */
int machSignalStackSizeOnStack; /* size of mach module sig stack */
int machSigStackSize; /* size of Sig_Stack structure */
int machSigStackOffsetOnStack; /* offset of sigStack field in
* MachSignalStack structure on the
* the user stack. */
int machSigStackOffsetInMach; /* offset to sigStack field in mach
* state structure. */
int machSigContextSize; /* size of Sig_Context structure */
int machSigContextOffsetOnStack; /* offset of sigContext field in
* MachSignalStack structure on the
* user stack. */
int machSigContextOffsetInMach; /* offset to sigContext field in mach
* state structure. */
int machSigUserStateOffsetOnStack; /* offset of machine-dependent field
* on the stack, called machContext,
* in the Sig_Context part of the
* MachSignalStack structure. */
int machSigTrapInstOffsetOnStack; /* offset of trapInst field in
* MachSignalStack on user stack. */
int machSigNumOffsetInSig; /* offset of sigNum field in
* Sig_Stack structure. */
int machSigAddrOffsetInSig; /* offset of sigAddr field in
* Sig_Stack structure. */
int machSigCodeOffsetInSig; /* offset of sigCode field in
* Sig_Stack structure. */
int machSigPCOffsetOnStack; /* offset of pcValue field in
* MachSignalStack on user stack. */
int machLastSysCallOffset; /* offset of lastSysCall field in
* Mach_State structure. */
Proc_ControlBlock *machFPUSaveProcPtr; /* Set to the Proc_ControlBlock of the
* process we are saving the FPU
* state for in Mach_Context switch. */
int machFPUSyncInst(); /* PC of stfsr instruction in
* context switch. */
int machFPUDumpSyncInst(); /* PC of stfsr instruction in
* MachDumpFPUState. */
/*
* Pointer to the state structure for the current process.
*/
Mach_State *machCurStatePtr = (Mach_State *)NIL;
char MachUnixAddr[] =
"Unix addr %x\n";
char MachUnixString[] =
"Unix syscall %d\n";
char MachUnixYesString[] =
"syscall success\n";
char MachUnixNoString[] =
"syscall failure\n";
char MachRunUserDeathString[] =
"MachRunUserProc: killing process!\n";
char MachHandleSignalDeathString[] =
"MachHandleSignal: killing process!\n";
char MachReturnFromSignalDeathString[] =
"MachReturnFromSignal: killing process!\n";
char MachReturnFromTrapDeathString[] =
"MachReturnFromTrap: killing process!\n";
char MachHandleWindowUnderflowDeathString[] =
"MachHandleWindowUnderflow: killing process!\n";
/*
* For testing correctness of defined offsets.
*/
#if 0
Mach_RegState testMachRegState;
Mach_State testMachState;
Proc_ControlBlock testPCB;
MachSignalStack testSignalStack;
Sig_Context testContext;
Sig_Stack testStack;
#endif
int debugCounter = 0; /* for debugging */
int debugSpace[500];
Address theAddrOfVmPtr = 0;
Address theAddrOfMachPtr = 0;
Address oldAddrOfVmPtr = 0;
Address oldAddrOfMachPtr = 0;
/*
* Make sure machRomVectorPtr is
* in the initialized data section.
* This is needed when transparent
* recovery is used.
*/
MachMonRomVector *machRomVectorPtr = (MachMonRomVector *) NIL;
MachMonBootParam machMonBootParam;
#ifdef sun4c
unsigned char *machInterruptReg;
unsigned int machClockRate;
struct idprom machIdProm = {0};
#endif
/*
* Make sure these variables are
* in the initialized data section.
*/
unsigned int machNumWindows = 0;
unsigned int machWimShift = 0; /* # windows - 1 */
/*
* Forward declarations.
*/
static void FlushTheWindows _ARGS_((int num));
static void HandleFPUException _ARGS_((Proc_ControlBlock *procPtr,
Mach_State *machStatePtr));
static void CheckFastRestart _ARGS_((void));
/*
* ----------------------------------------------------------------------------
*
* Mach_Init --
*
* Initialize some stuff.
*
* Results:
* None.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
Mach_Init()
{
int i;
extern void MachVectoredInterrupt();
extern void MachHandleDebugTrap();
int offset;
/*
* Set exported machine dependent variables.
*/
mach_KernStart = (Address)MACH_KERN_START;
mach_KernEnd = (Address)MACH_KERN_END;
mach_CodeStart = (Address)MACH_CODE_START;
mach_StackBottom = (Address)MACH_STACK_BOTTOM;
mach_KernStackSize = MACH_KERN_STACK_SIZE;
mach_FirstUserAddr = (Address)MACH_FIRST_USER_ADDR;
mach_LastUserAddr = (Address)MACH_LAST_USER_ADDR;
mach_MaxUserStackAddr = (Address)MACH_MAX_USER_STACK_ADDR;
mach_LastUserStackPage = MACH_LAST_USER_STACK_PAGE;
#define CHECK_SIZE(c, d) \
if (sizeof (c) != d) { \
panic("Bad size for structure. Redo machConst.h!\n");\
}
#define CHECK_OFFSETS(s, o) \
if (offsetof(Mach_State, s) != o) { \
panic("Bad offset for registers. Redo machConst.h!\n");\
}
#define CHECK_TRAP_REG_OFFSETS(s, o) \
if (offsetof(Mach_RegState, s) != o) { \
panic("Bad offset for trap registers. Redo machConst.h!\n");\
}
CHECK_SIZE(Mach_RegState, MACH_SAVED_STATE_FRAME);
CHECK_OFFSETS(trapRegs, MACH_TRAP_REGS_OFFSET);
CHECK_OFFSETS(switchRegs, MACH_SWITCH_REGS_OFFSET);
CHECK_OFFSETS(savedRegs[0][0], MACH_SAVED_REGS_OFFSET);
CHECK_OFFSETS(savedMask, MACH_SAVED_MASK_OFFSET);
CHECK_OFFSETS(savedSps[0], MACH_SAVED_SPS_OFFSET);
CHECK_OFFSETS(kernStackStart, MACH_KSP_OFFSET);
CHECK_OFFSETS(fpuStatus, MACH_FPU_STATUS_OFFSET)
CHECK_TRAP_REG_OFFSETS(curPsr, MACH_LOCALS_OFFSET);
CHECK_TRAP_REG_OFFSETS(ins[0], MACH_INS_OFFSET);
CHECK_TRAP_REG_OFFSETS(globals[0], MACH_GLOBALS_OFFSET);
CHECK_TRAP_REG_OFFSETS(fsr, MACH_FPU_FSR_OFFSET);
CHECK_TRAP_REG_OFFSETS(numQueueEntries, MACH_FPU_QUEUE_COUNT);
CHECK_TRAP_REG_OFFSETS(fregs[0], MACH_FPU_REGS_OFFSET);
CHECK_TRAP_REG_OFFSETS(fqueue[0], MACH_FPU_QUEUE_OFFSET);
#ifdef sun4c
if (romVectorPtr->v_romvec_version < 2) {
if ((*(romVectorPtr->virtMemory))->address !=
(unsigned) VMMACH_DEV_START_ADDR ||
((unsigned) VMMACH_DEV_START_ADDR +
(*(romVectorPtr->virtMemory))->size - 1)
!= (unsigned) VMMACH_DEV_END_ADDR) {
panic("VMMACH_DEV_START_ADDR and VMMACH_DEV_END_ADDR are wrong.\n");
}
}
#endif /* sun4c */
#undef CHECK_SIZE
#undef CHECK_OFFSETS
/*
* Initialize some of the dispatching information. The rest is initialized
* by Mach_InitSysCall, below.
*/
/*
* Get offset of machStatePtr in proc control blocks. This one is
* subject to a different module, so it's easier not to use a constant.
*/
machStatePtrOffset = offsetof(Proc_ControlBlock, machStatePtr);
machKcallTableOffset = offsetof(Proc_ControlBlock, kcallTable);
machSpecialHandlingOffset = offsetof(Proc_ControlBlock, specialHandling);
if (MACH_UNIX_ERRNO_OFFSET != offsetof(Proc_ControlBlock, unixErrno)) {
panic("MACH_UNIX_ERRNO_OFFSET is wrong!\n");
}
machMaxSysCall = -1;
MachPIDOffset = offsetof(Proc_ControlBlock, processID);
machGenFlagsOffset = offsetof(Proc_ControlBlock, genFlags);
machForeignFlag = PROC_FOREIGN;
/*
* Initialize all the horrid offsets for dealing with getting stuff from
* signal things in the mach state structure to signal things on the user
* stack.
*/
machSignalStackSizeOnStack = sizeof (MachSignalStack);
if ((machSignalStackSizeOnStack & 0x7) != 0) {
panic("MachSignalStack struct must be a multiple of double-words!\n");
}
machSigStackSize = sizeof (Sig_Stack);
machSigStackOffsetOnStack = offsetof(MachSignalStack, sigStack);
machSigStackOffsetInMach = offsetof(Mach_State, sigStack);
machSigContextSize = sizeof (Sig_Context);
machSigContextOffsetOnStack = offsetof(MachSignalStack, sigContext);
machSigContextOffsetInMach = offsetof(Mach_State, sigContext);
machSigUserStateOffsetOnStack = offsetof(MachSignalStack,
sigContext.machContext.userState);
machSigTrapInstOffsetOnStack = offsetof(MachSignalStack,
sigContext.machContext.trapInst);
machSigNumOffsetInSig = offsetof(Sig_Stack, sigNum);
machSigAddrOffsetInSig = offsetof(Sig_Stack, sigAddr);
machSigCodeOffsetInSig = offsetof(Sig_Stack, sigCode);
machSigPCOffsetOnStack = offsetof(MachSignalStack,
sigContext.machContext.pcValue);
machLastSysCallOffset = offsetof(Mach_State, lastSysCall);
/*
* base of the debugger stack
*/
machDebugStackStart = (unsigned int) mach_DebugStack +
sizeof (mach_DebugStack);
/*
* Initialize the interrupt vector table.
*/
for (i = 0; i < MACH_NUM_VECTORS; i++) {
#ifndef sun4c
if (i == 13 || i == 11 || i == 9 || i == 7 || i == 5 || i == 3 ||
i == 2) {
machVectorTable[i] = (Address) MachVectoredInterrupt;
/*
* Set arg to vme vector address for this trap level.
* In the high bits of the address, we want MACH_VME_INTR_VECTOR.
* In bits 3 to 1 we want the VME bus level. We want bit 0 to be
* 1. So, for example, for interrupt level 13, we want to put
* VME level7 into bits 3 to 1 and then set bit 0 high. But this
* is equivalent to putting the number 14 into bits 3 to 0 and then
* setting bit 0 high. So this means adding 1 to the interrupt
* level and then setting bit 0 high.
*/
machInterruptArgs[i] = (ClientData)
(MACH_VME_INTR_VECTOR | ((i + 1) | 1));
} else {
#endif
machVectorTable[i] = (Address) MachHandleDebugTrap;
machInterruptArgs[i] = (ClientData) 0;
#ifndef sun4c
}
#endif
}
/* Temporary: for debugging net module and debugger: */
mach_NumDisableInterrupts[0] = 1;
#ifdef sun4c
if (romVectorPtr->v_romvec_version < 2) {
#endif
/*
* Copy the boot parameter structure. The original location will get
* unmapped during vm initialization so we need to get our own copy.
*/
machMonBootParam = **(romVectorPtr->bootParam);
offset = (unsigned int) *(romVectorPtr->bootParam) -
(unsigned int) &(machMonBootParam);
for (i = 0; i < 8; i++) {
if (machMonBootParam.argPtr[i] != (char *) 0 &&
machMonBootParam.argPtr[i] != (char *) NIL) {
machMonBootParam.argPtr[i] -= offset;
}
}
#ifdef sun4c
}
#endif
#ifndef sun4c
/*
* Clear out the line input buffer to the prom so we don't get extra
* characters at the end of shorter reboot strings.
*/
bzero((char *)(romVectorPtr->lineBuf), *romVectorPtr->lineSize);
#endif
#ifdef sun4c
if (Mach_MonSearchProm("*", "clock-frequency",
(char *)&machClockRate,
sizeof machClockRate) != sizeof machClockRate) {
panic("Clock rate not found.\n");
}
/*
* Keep clock rate precision to 1 decimal place.
*/
machClockRate /= 100000;
#ifdef NOTDEF /* No need to print this stuff. */
Mach_MonPrintf("PROM: Clock rate is %d.%dMHz\n",
machClockRate / 10, machClockRate % 10);
#endif /* NOTDEF */
if (Mach_MonSearchProm("interrupt-enable", "address",
(char *)&machInterruptReg,
sizeof machInterruptReg) != sizeof machInterruptReg) {
panic("Interrupt register not found.\n");
}
#ifdef NOTDEF /* No need to print this stuff. */
Mach_MonPrintf("PROM: Interrupt register is at %x\n", machInterruptReg);
#endif /* NOTDEF */
/*
* This gets turned on by the profiler init when it is called.
*/
*Mach_InterruptReg &= ~MACH_ENABLE_LEVEL14_INTR;
#endif
if (recov_Transparent) {
CheckFastRestart();
}
return;
}
/*
*----------------------------------------------------------------------
*
* CheckFastRestart --
*
* Check if enough space was allocated for the fast restart.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
CheckFastRestart()
{
char *src, *dest;
int count;
extern int edata;
extern int etext();
src = (char *) &etext;
dest = (char *) &edata;
count = (unsigned int) dest - (unsigned int) src;
if (count > MACH_RESTART_DATA_SIZE) {
Mach_MonPrintf("Not enough restart space saved for kernel data.\n");
Mach_MonAbort();
}
return;
}
/*
*----------------------------------------------------------------------
*
* Mach_GetRestartTableSize --
*
* Return the size allocated for the fast restart table area.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Mach_GetRestartTableSize()
{
return MACH_RESTART_TABLE_SIZE;
}
/*
*----------------------------------------------------------------------
*
* Mach_InitFirstProc --
*
* Initialize the machine state struct for the very first process.
*
* Results:
* None.
*
* Side effects:
* Machine info allocated and stack start set up.
*
*----------------------------------------------------------------------
*/
void
Mach_InitFirstProc(procPtr)
Proc_ControlBlock *procPtr;
{
procPtr->machStatePtr = (Mach_State *)Vm_RawAlloc(sizeof(Mach_State));
bzero((char *)(procPtr->machStatePtr), sizeof (Mach_State));
procPtr->machStatePtr->kernStackStart = mach_StackBottom;
procPtr->machStatePtr->trapRegs = (Mach_RegState *) NIL;
procPtr->machStatePtr->switchRegs = (Mach_RegState *) NIL;
machCurStatePtr = procPtr->machStatePtr;
}
/*
*----------------------------------------------------------------------
*
* Mach_SetupNewState --
*
* Initialize the machine state for this process. This includes
* allocating and initializing a kernel stack. Assumed that will
* be called when starting a process after a fork or restarting a
* process after a migration.
*
* Results:
* PROC_NO_STACKS if couldn't allocate a kernel stack. SUCCESS otherwise.
*
* Side effects:
* Machine state in the destination process control block is overwritten.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Mach_SetupNewState(procPtr, fromStatePtr, startFunc, startPC, user)
Proc_ControlBlock *procPtr; /* Pointer to process control block
* to initialize state for. */
Mach_State *fromStatePtr; /* State of parent on fork or from
* other machine on migration. */
void (*startFunc)(); /* Function to call when process first
* starts executing. */
Address startPC; /* Address to pass as argument to
* startFunc. If NIL then the address
* is taken from *fromStatePtr's
* exception stack. */
Boolean user; /* TRUE if is a user process. */
{
register Mach_RegState *stackPtr;
register Mach_State *statePtr;
/*
* If it's a user process forking, we must make sure all its windows have
* been saved to the stack so that when the register state and the stack
* are copied to the new process, it will get the real stuff.
*/
if (user) {
Mach_DisableIntr();
Mach_FlushWindowsToStack();
Mach_EnableIntr();
}
if (procPtr->machStatePtr == (Mach_State *)NIL) {
procPtr->machStatePtr = (Mach_State *)Vm_RawAlloc(sizeof(Mach_State));
}
bzero((char *) procPtr->machStatePtr, sizeof (Mach_State));
statePtr = procPtr->machStatePtr;
statePtr->trapRegs = (Mach_RegState *)NIL;
/*
* Allocate a kernel stack for this process.
*/
statePtr->kernStackStart = (Address) Vm_GetKernelStack(0);
if (statePtr->kernStackStart == (Address)NIL) {
return(PROC_NO_STACKS);
}
/*
* Pointer to context switch register's save area is also a pointer
* to the top of the stack, since the regs are saved there.
* If this is a kernel process, we only need space MACH_SAVED_STATE_FRAME
* + MACH_FULL_STACK_FRAME, but if it's a user process we need more:
* (2 * MACH_SAVED_STATE_FRAME). Both types of processes need the
* first MACH_SAVED_STATE_FRAME for their context switch regs area. A
* kernel process then only needs space under that on its stack for its
* first routine to store its arguments in its "caller's" stack frame, so
* this extra space just fakes a caller's stack frame. But for a user
* process, we have trap regs. And these trapRegs get stored under
* the context switch regs on the kernel stack.
*/
statePtr->switchRegs = (Mach_RegState *)((statePtr->kernStackStart) +
MACH_KERN_STACK_SIZE - (2 * MACH_SAVED_STATE_FRAME));
statePtr->switchRegs = (Mach_RegState *)
(((unsigned int)(statePtr->switchRegs)) & ~0x7); /* should be okay already */
/*
* Initialize the stack so that it looks like it is in the middle of
* Mach_ContextSwitch.
*/
stackPtr = statePtr->switchRegs; /* stack pointer is set from this. */
/*
* Fp is set to saved window area for window we'll return to. The area for
* the window of Mach_ContextSwitch is a Mach_RegState. Below this on
* the stack (at higher address than) is the saved window area of the
* routine we'll return to from Mach_ContextSwitch. So the fp must be
* set to the top of this saved window area.
*/
*((Address *)(((Address)stackPtr) + MACH_FP_OFFSET)) =
((Address)stackPtr) + MACH_SAVED_STATE_FRAME;
/*
* We are to return to startFunc from Mach_ContextSwitch, but
* Mach_ContextSwitch will do a return to retPC + 8, so we subtract
* 8 from it here to get to the right place.
*/
*((Address *)(((Address)stackPtr) + MACH_RETPC_OFFSET)) =
((Address)startFunc) - 8;
/*
* Set the psr to restore to have traps enabled and interrupts off.
*/
stackPtr->curPsr = MACH_HIGH_PRIO_PSR;
/*
* Set up the state of the process. User processes inherit from their
* parent or the migrated process. If the PC is not specified, take it
* from the parent as well.
*/
if (user) {
/*
* Trap state regs are the same for child process.
*/
statePtr->trapRegs = (Mach_RegState *)
(((Address) stackPtr) + MACH_SAVED_STATE_FRAME);
bcopy((Address)fromStatePtr->trapRegs, (Address)statePtr->trapRegs,
sizeof (Mach_RegState));
/*
* Check to see if any register windows were saved to internal buffer
* in Mach_FlushWindowsToStack(), above. If so, copy the buffer state
* to the new process, so that when it returns from the fork trap, it
* will copy out the saved windows to its stack.
*/
if (fromStatePtr->savedMask != 0) {
procPtr->specialHandling = 1;
statePtr->savedMask = fromStatePtr->savedMask;
bcopy((Address) fromStatePtr->savedRegs,
(Address) statePtr->savedRegs,
sizeof (fromStatePtr->savedRegs));
bcopy((Address) fromStatePtr->savedSps,
(Address) statePtr->savedSps,
sizeof (fromStatePtr->savedSps));
}
}
if (startPC == (Address)NIL) {
*((Address *)(((Address)stackPtr) + MACH_ARG0_OFFSET)) =
(Address) fromStatePtr->trapRegs->pc;
} else {
/*
* The first argument to startFunc is supposed to be startPC. But that
* would be in an in register in startFunc's window which is one before
* Mach_ContextSwitch's window. But startFunc will do a save operation
* at the beginning so it will actually be executing in Mach_ContextS's
* window, so arg0 to startFunc must actually be arg0 that is restored
* at the end of Mach_ContextSwitch, so we have to put it in the in
* register area of Mach_RegState area on the stack. Weird.
*/
*((Address *)(((Address)stackPtr) + MACH_ARG0_OFFSET)) = startPC;
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* Mach_SetReturnVal --
*
* Set the return value for a process from a system call. Intended to
* be called by the routine that starts a user process after a fork.
* Interrupts must be off here!
*
* Results:
* None.
*
* Side effects:
* Register D0 is set in the user registers.
*
*----------------------------------------------------------------------
*/
void
Mach_SetReturnVal(procPtr, retVal, retVal2)
Proc_ControlBlock *procPtr; /* Process to set return value for. */
int retVal; /* Value for process to return. */
int retVal2; /* 2nd Value for process to return. */
{
if (procPtr->machStatePtr->trapRegs == (Mach_RegState *) NIL ||
procPtr->machStatePtr->trapRegs == (Mach_RegState *) 0) {
return;
}
procPtr->machStatePtr->trapRegs->ins[0] = retVal;
procPtr->machStatePtr->trapRegs->ins[1] = retVal2;
return;
}
/*
*----------------------------------------------------------------------
*
* Mach_StartUserProc --
*
* Start a user process executing for the first time.
* Interrupts must be off here!
*
* Results:
* None.
*
* Side effects:
* Stack pointer and the program counter set for the process and
* the current process's image is replaced.
*
*----------------------------------------------------------------------
*/
void
Mach_StartUserProc(procPtr, entryPoint)
Proc_ControlBlock *procPtr; /* Process control block for process
* to start. */
Address entryPoint; /* Where process is to start
* executing. */
{
register Mach_State *statePtr;
statePtr = procPtr->machStatePtr;
/*
* MachRunUserProc will put the values from our trap regs into the actual
* registers so that we'll be in shape to rett back to user mode.
*/
Mach_DisableIntr();
/*
* Return from trap pc.
*/
statePtr->trapRegs->pc = (unsigned int) entryPoint;
statePtr->trapRegs->nextPc = (unsigned int) (entryPoint + 4);
MachRunUserProc();
/* THIS DOES NOT RETURN */
}
/*
*----------------------------------------------------------------------
*
* Mach_ExecUserProc --
*
* Replace the calling user process's image with a new one.
*
* Results:
* None.
*
* Side effects:
* Stack pointer set for the process.
*
*----------------------------------------------------------------------
*/
void
Mach_ExecUserProc(procPtr, userStackPtr, entryPoint)
Proc_ControlBlock *procPtr; /* Process control block for
* process to exec. */
Address userStackPtr; /* Stack pointer for when the
* user process resumes
* execution. */
Address entryPoint; /* Where the user process is
* to resume execution. */
{
Mach_RegState tmpTrapState;
/*
* We do not call DISABLE_INTR here because there's an implicit enable
* of interrupts in MachRunUserProc().
*/
Mach_DisableIntr();
machCurStatePtr = procPtr->machStatePtr;
/*
* Since we're not returning, we can just use this space on our kernel
* stack as trapRegs. This is safe, since we only fill in the fp, tbr,
* pc, and nextPc fields (in Mach_StartUserProc()) and these just touch
* the saved-window section of our stack and won't mess up any of our
* arguments.
*/
procPtr->machStatePtr->trapRegs = &tmpTrapState;
/*
* The user stack pointer gets MACH_FULL_STACK_FRAME subtracted from it
* so that the user stack has space for its first routine to store its
* arguments in its caller's stack frame. (So we create a fake caller's
* stack frame this way.)
*/
procPtr->machStatePtr->trapRegs->ins[MACH_FP_REG] = (unsigned int)
(userStackPtr - MACH_FULL_STACK_FRAME);
procPtr->machStatePtr->trapRegs->curPsr = MACH_FIRST_USER_PSR;
procPtr->machStatePtr->trapRegs->pc = (unsigned int) entryPoint;
procPtr->machStatePtr->trapRegs->tbr = (unsigned int) machTBRAddr;
/*
* Initialized the floating point state.
*/
procPtr->machStatePtr->fpuStatus = 0;
/*
* Return value is cleared for exec'ing user process. This shouldn't
* matter since a good exec won't return.
*/
procPtr->machStatePtr->trapRegs->ins[0] = 0;
Mach_StartUserProc(procPtr, entryPoint);
/* THIS DOES NOT RETURN */
}
/*
*----------------------------------------------------------------------
*
* Mach_FreeState --
*
* Free up the machine state for the given process control block.
*
* Results:
* None.
*
* Side effects:
* Free up the kernel stack.
*
*----------------------------------------------------------------------
*/
void
Mach_FreeState(procPtr)
Proc_ControlBlock *procPtr; /* Process control block to free
* machine state for. */
{
if (procPtr->machStatePtr->kernStackStart != (Address)NIL) {
Vm_FreeKernelStack(procPtr->machStatePtr->kernStackStart);
procPtr->machStatePtr->kernStackStart = (Address)NIL;
}
}
/*
*----------------------------------------------------------------------
*
* Mach_GetDebugState --
*
* Extract the appropriate fields from the machine state struct
* and store them into the debug struct.
*
* Results:
* None.
*
* Side effects:
* Debug struct filled in from machine state struct.
*
*----------------------------------------------------------------------
*/
void
Mach_GetDebugState(procPtr, debugStatePtr)
Proc_ControlBlock *procPtr;
Proc_DebugState *debugStatePtr;
{
register Mach_State *machStatePtr;
machStatePtr = procPtr->machStatePtr;
bcopy((Address)machStatePtr->trapRegs,
(Address)(&debugStatePtr->regState), sizeof(Mach_DebugState));
return;
}
/*
*----------------------------------------------------------------------
*
* Mach_SetDebugState --
*
* Extract the appropriate fields from the debug struct
* and store them into the machine state struct.
*
* Results:
* None.
*
* Side effects:
* Machine state struct filled in from the debug state struct.
*
*----------------------------------------------------------------------
*/
void
Mach_SetDebugState(procPtr, debugStatePtr)
Proc_ControlBlock *procPtr;
Proc_DebugState *debugStatePtr;
{
register Mach_State *machStatePtr;
/* y, pc's g1-g7 all in's*/
machStatePtr = procPtr->machStatePtr;
machStatePtr->trapRegs->pc = debugStatePtr->regState.pc;
machStatePtr->trapRegs->nextPc = debugStatePtr->regState.nextPc;
machStatePtr->trapRegs->y = debugStatePtr->regState.y;
bcopy((Address)debugStatePtr->regState.ins,
(Address)machStatePtr->trapRegs->ins, MACH_NUM_INS * sizeof (int));
bcopy((Address)debugStatePtr->regState.globals,
(Address)machStatePtr->trapRegs->globals,
MACH_NUM_GLOBALS * sizeof (int));
return;
}
/*
*----------------------------------------------------------------------
*
* Mach_InitSyscall --
*
* During initialization, this procedure is called once for each
* kernel call, in order to set up information used to dispatch
* the kernel call. This procedure must be called once for each
* kernel call, in order starting at 0.
*
* Results:
* None.
*
* Side effects:
* Initializes the dispatch tables for the kernel call.
*
*----------------------------------------------------------------------
*/
void
Mach_InitSyscall(callNum, numArgs, normalHandler, migratedHandler)
int callNum; /* Number of the system call. */
int numArgs; /* Number of one-word arguments passed
* into call on stack. */
ReturnStatus (*normalHandler)(); /* Procedure to process kernel call
* when process isn't migrated. */
ReturnStatus (*migratedHandler)(); /* Procedure to process kernel call
* for migrated processes. */
{
machMaxSysCall++;
if (machMaxSysCall != callNum) {
printf("Warning: out-of-order kernel call initialization, call %d\n",
callNum);
}
if (machMaxSysCall >= SYS_NUM_SYSCALLS) {
printf("Warning: too many kernel calls.\n");
machMaxSysCall--;
return;
}
if (numArgs > SYS_MAX_ARGS) {
printf("Warning: too many arguments to kernel call %d\n", callNum);
numArgs = SYS_MAX_ARGS;
}
/*
* Offset of beginning of args on stack - offset from frame pointer.
* Figure out offset from fp of params past the sixth.
* It copies from last arg to first
* arg in that order, so we start offset at bottom of last arg.
*/
/*
* TURN THESE INTO PROPER CONSTANTS!
*/
/*
* We copy going towards higher addresses, rather than lower, as the sun3
* does it. So our offset is at top of extra parameters to copy, and not
* below them (stack-wise speaking, not address-wise speaking).
*/
machArgOffsets[machMaxSysCall] = MACH_SAVED_WINDOW_SIZE +
MACH_ACTUAL_HIDDEN_AND_INPUT_STORAGE;
/*
* Where to jump to in fetching routine to copy the right amount from
* the stack. The fewer args we have, the farther we jump... Figure out
* how many are in registers, then do rest from stack. There's instructions
* to copy 10 words worth, for now, since 6 words worth of arguments are
* in the input registers. If this number changes, change machTrap.s
* and the jump offset below!
*/
if (numArgs <= 6) {
machArgDispatch[machMaxSysCall] = (Address) MachFetchArgsEnd;
} else {
machArgDispatch[machMaxSysCall] = (10 - (numArgs - 6))*16 +
((Address)MachFetchArgs);
}
mach_NormalHandlers[machMaxSysCall] = normalHandler;
mach_MigratedHandlers[machMaxSysCall] = migratedHandler;
}
/*
* ----------------------------------------------------------------------------
*
* Mach_SetHandler --
*
* This is used both for autovectored devices and for regular interrupt
* routines for device interrupt levels. For autovectored devices,
* the routine MachVectoredInterrupt will already have been installed
* for the auto-vectored interrupt levels. Then this routine should be
* be called with the interrrupt vector for the device and its
* real interrupt handler. For non-autovectored interrupt handlers, the
* handler should just be installed with a vector that is the
* device's interrupt level.
*
* Results:
* None.
*
* Side effects:
* The exception vector table is modified.
*
* ----------------------------------------------------------------------------
*/
void
Mach_SetHandler(vectorNumber, handler, clientData)
int vectorNumber; /* Vector number that the device generates */
int (*handler)(); /* Interrupt handling procedure */
ClientData clientData; /* ClientData for interrupt callback routine. */
{
if (vectorNumber < 0 || vectorNumber > 255) {
panic("Warning: Bad vector number %d\n", vectorNumber);
} else {
machVectorTable[vectorNumber] = (Address) handler;
machInterruptArgs[vectorNumber] = (ClientData) clientData;
}
}
/*
* ----------------------------------------------------------------------------
*
* Routines to set up and return from signal handlers.
*
* In order to call a handler four things must be done:
*
* 1) The current state of the process must be saved so that when
* the handler returns a normal return to user space can occur.
* 2) The user stack must be set up so that the signal number and the
* the signal code are passed to the handler.
* 3) Things must be set up so that when the handler returns it returns
* back into the kernel so that state can be cleaned up.
* 4) The trap stack that was created when the kernel was entered and is
* used to return a process to user space must be modified so that
* the signal handler is called instead of executing the
* normal return.
*
* The last one is done by simply changing the program counter where the
* user process will execute on return to be the address of the signal
* handler and the user stack pointer to point to the proper place on
* the user stack. The first three of these are accomplished by
* setting up the user stack properly. The top entry on the stack is the
* return address where the handler will start executing upon return. But
* this is just the address of a trap instruction that is stored on the stack
* below. Thus when a handler returns it will execute a trap instruction
* and drop back into the kernel.
*/
/*
* ----------------------------------------------------------------------------
*
* MachCallSigReturn --
*
* Process a return from a signal handler. Call the Sig_Return
* routine with appropriate args.
*
* Results:
* None.
*
* Side effects:
* Whatever Sig_Return does.
*
* ----------------------------------------------------------------------------
*/
void
MachCallSigReturn()
{
Proc_ControlBlock *procPtr;
Mach_State *statePtr;
Sig_Stack *sigStackPtr;
procPtr = Proc_GetCurrentProc();
statePtr = procPtr->machStatePtr;
sigStackPtr = &(statePtr->sigStack);
sigStackPtr->contextPtr = &(statePtr->sigContext);
/*
* Take the proper action on return from a signal.
*/
Sig_Return(procPtr, sigStackPtr);
}
/*
* ----------------------------------------------------------------------------
*
* Mach_ProcessorState --
*
* Determines what state the processor is in.
*
* Results:
* MACH_USER if was at user level
* MACH_KERNEL if was at kernel level
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
Mach_ProcessorStates
Mach_ProcessorState(processor)
int processor; /* processor number for which info is requested */
{
if (mach_KernelMode) {
return(MACH_KERNEL);
} else {
return(MACH_USER);
}
}
/*
* ----------------------------------------------------------------------------
*
* Mach_GetMachineArch --
*
* Return the machine architecture (SYS_SUN2 or SYS_SUN3).
*
* Results:
* The machine architecture.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
int
Mach_GetMachineArch()
{
# ifdef sun2
return SYS_SUN2;
# endif sun2
# ifdef sun3
return SYS_SUN3;
# endif sun3
# ifdef sun4
return SYS_SUN4;
# endif sun4
}
/*
* ----------------------------------------------------------------------------
*
* Mach_CheckSpecialHandling--
*
* Forces a processor to check the special handling flag of a process.
* This should only be called on a multiprocessor.
*
* Results:
* None.
*
* Side effects:
* None.
*
* ----------------------------------------------------------------------------
*/
void
Mach_CheckSpecialHandling(pnum)
int pnum; /* Processor number. */
{
panic("Mach_CheckSpecialHandling called for processor %d\n",pnum);
}
/*
*----------------------------------------------------------------------
*
* Mach_GetNumProcessors() --
*
* Return the number of processors in the system. NOTE: This should
* really be in a machine-independent area of the mach module. Note
* further: if this is used only as a system call, it should return
* a ReturnStatus!
*
* Results:
* The number of processors is returned.
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
int
Mach_GetNumProcessors()
{
return (mach_NumProcessors);
}
/*
*----------------------------------------------------------------------
*
* MachPageFault() -
*
* Handle a page fault.
*
* Results:
* None
*
* Side effects:
* A page causing a memory access error is made valid. If it's an
* illegal page fault in the kernel, we will call panic.
*
*----------------------------------------------------------------------
*/
void
MachPageFault(busErrorReg, addrErrorReg, trapPsr, pcValue)
unsigned int busErrorReg;
Address addrErrorReg;
unsigned int trapPsr;
Address pcValue;
{
Proc_ControlBlock *procPtr;
Boolean protError;
Boolean copyInProgress = FALSE;
ReturnStatus status;
extern int VmMachQuickNDirtyCopy();
extern int VmMachEndQuickCopy();
/*
* Are we in quick cross-context copy routine? If so, we can't page fault
* in it.
*/
if ((pcValue >= (Address) VmMachQuickNDirtyCopy) &&
(pcValue < (Address) VmMachEndQuickCopy)) {
/*
* This doesn't return to here. It erases the fact that the
* page fault happened and makes the copy routine that
* got the page fault return FAILURE to its caller. We must turn off
* interrupts before calling MachHandleBadQuickCopy().
*/
Mach_DisableIntr();
MachHandleBadQuickCopy();
Mach_EnableIntr();
}
/*
* Are we poking at or peeking into memory-mapped devices?
* We must check this before looking for the current process, since this
* can happen during boot-time before we have set up processes.
*/
if ((pcValue >= (Address) MachProbeStart) &&
(pcValue < (Address) MachProbeEnd)) {
/*
* This doesn't return to here. It erases the fact that the
* page fault happened and makes the probe routine that
* got the page fault return FAILURE to its caller. We must turn off
* interrupts before calling MachHandleBadProbe().
*/
Mach_DisableIntr();
MachHandleBadProbe();
Mach_EnableIntr();
}
#ifdef sun4
/*
* On the sun4/200 with the Jaguar HBA we get VME timeout errors from
* the board. This code retries the error up to 100000 times before droping
* into the code below which panics.
* These errors happen a lot with the RAID HPPI boards, so we disable
* the printout.
*/
{
static timeoutRetryCount = 0;
extern void MachVectoredInterruptLoad();
if ((trapPsr & MACH_PS_BIT) && (busErrorReg&MACH_TIMEOUT_ERROR)) {
/*
* If the error occurred on a the load of the interrupt
* vector make the routine return.
*/
if (pcValue == (Address) MachVectoredInterruptLoad) {
/*
* This doesn't return to here. It erases the fact that the
* page fault happened and makes the MachVectoredInterrupt
* routine that got the page fault return FAILURE
* to its caller.
*/
Mach_MonPrintf(
"MachPageFault: Bus timeout error on VME interrupt vector load pc:0x%x, addr:0x%x\n",
pcValue, addrErrorReg);
MachHandleBadQuickCopy();
}
#if 0
Mach_MonPrintf(
"MachPageFault: Bus timeout error retry %d at pc:0x%x, addr:0x%x\n",
timeoutRetryCount, pcValue, addrErrorReg);
#endif
if (timeoutRetryCount < 1000000) {
timeoutRetryCount++;
return;
}
}
timeoutRetryCount = 0;
}
/* We used to enable interrupts before we called this routine, but we
* don't want them enabled if it is a VME bus timeout, so we enable them
* now */
Mach_EnableIntr();
#endif /* sun4 */
procPtr = Proc_GetActualProc();
if (procPtr == (Proc_ControlBlock *) NIL) {
panic(
"MachPageFault: Current process is NIL!! Trap pc is 0x%x, addr 0x%x\n",
(unsigned) pcValue, addrErrorReg);
}
/* process kernel page fault */
if (trapPsr & MACH_PS_BIT) { /* kernel mode before trap */
if (!(procPtr->genFlags & PROC_USER)) {
/*
* This fault happened inside the kernel and it wasn't on behalf
* of a user process. This is an error.
*/
panic(
"MachPageFault: page fault in kernel process! pc:0x%x, addr:0x%x, Error:0x%x\n",
pcValue, addrErrorReg, busErrorReg);
}
/*
* A page fault on a user process while executing in
* the kernel. This can happen when information is
* being copied back and forth between kernel and user state
* (indicated by particular values of the program counter).
*/
if ((pcValue >= (Address) Vm_CopyIn) &&
(pcValue < (Address) VmMachCopyEnd)) {
copyInProgress = TRUE;
} else if ((pcValue >= (Address) MachFetchArgs) &&
(pcValue <= (Address) MachFetchArgsEnd)) {
copyInProgress = TRUE;
} else if (procPtr->vmPtr->numMakeAcc == 0) {
/*
* ERROR: pc faulted in a bad place!
*/
panic(
"MachPageFault: kernel page fault at illegal pc: 0x%x, addr 0x%x\n",
pcValue, addrErrorReg);
}
protError = (busErrorReg & MACH_PROT_ERROR);
/*
* Try to fault in the page.
*/
status = Vm_PageIn(addrErrorReg, protError);
if (status != SUCCESS) {
if (copyInProgress) {
/*
* This doesn't return to here. It erases the fact that the
* page fault happened and makes the copy routine that
* got the page fault return SYS_ARG_NO_ACCESS to its caller.
* We must turn off interrupts before calling
* MachHandleBadArgs().
*/
Mach_DisableIntr();
MachHandleBadArgs();
Mach_EnableIntr();
} else {
/* kernel error */
panic(
"MachPageFault: couldn't page in kernel page at 0x%x, pc 0x%x\n",
addrErrorReg, pcValue);
}
}
return;
}
/* user page fault */
protError = busErrorReg & MACH_PROT_ERROR;
if (Vm_PageIn(addrErrorReg, protError) != SUCCESS) {
printf(
"MachPageFault: Bus error in user proc %x, PC = %x, addr = %x BR Reg %x\n",
#ifdef sun4c
procPtr->processID, pcValue, addrErrorReg, busErrorReg);
#else
procPtr->processID, pcValue, addrErrorReg, (short) busErrorReg);
#endif
/* Kill user process */
Sig_Send(SIG_ADDR_FAULT, SIG_ACCESS_VIOL, procPtr->processID, FALSE,
(Address)addrErrorReg);
return;
}
return;
}
/*
*----------------------------------------------------------------------
*
* MachUserAction() -
*
* Check what sort of user-process action is needed. We already know
* that some sort of action is needed, since the specialHandling flag
* should be checked before calling this routine. The possible actions
* are to take a context switch, to push saved user windows from the mach
* state structure out to the user stack, or to set things up to handle
* pending signals. We assume traps are enabled before this routine is
* called.
*
* Results:
* The return value 0 indicates we have no pending signal.
* The return value 1 indicates we have a pending Sprite signal.
* The return value 2 indicates we have a pending Unix signal.
*
* Side effects:
* The mach state structure may change, especially the mask that indicates
* which windows were saved into the internal buffer.
*
*----------------------------------------------------------------------
*/
int
MachUserAction()
{
Proc_ControlBlock *procPtr;
Mach_State *machStatePtr;
Sig_Stack *sigStackPtr;
Address pc;
int unixSignal;
int restarted=0;
procPtr = Proc_GetCurrentProc();
if (procPtr->unixProgress != PROC_PROGRESS_NOT_UNIX &&
procPtr->unixProgress != PROC_PROGRESS_UNIX && debugProcStubs) {
printf("UnixProgress = %d entering MachUserReturn\n",
procPtr->unixProgress);
}
HandleItAgain:
if (procPtr->Prof_Scale != 0 && procPtr->Prof_PC != 0) {
Prof_RecordPC(procPtr);
}
procPtr->specialHandling = 0;
/*
* Take a context switch if one is pending for this process.
* If other stuff, such as flushing the windows to the stack needs to
* be done, it will happen when the process is switched back in again.
* We came from MachReturnFromTrap, where interrupts were off, so we
* must turn them on.
*/
Mach_EnableIntr();
if (procPtr->schedFlags & SCHED_CONTEXT_SWITCH_PENDING) {
Sched_LockAndSwitch();
}
machStatePtr = procPtr->machStatePtr;
/*
* Save the windows that were stored in internal buffers to the user stack.
* The windows were saved to internal buffers due to the user stack not
* being resident. The overflow handler can't take page faults, but
* we can.
*/
if (machStatePtr->savedMask != 0) {
int i;
for (i = 0; i < MACH_NUM_WINDOWS; i++) {
if ((1 << i) & machStatePtr->savedMask) {
/*
* Clear the mask for this window. We must turn off interrupts
* to do this, since changing the savedMask must be an
* atomic operation. If it weren't, and an interrupt came
* in that caused us to save some other window to the stack
* after we have read the savedMask, we would overwrite the
* fact when storing the saved Mask...
*/
Mach_DisableIntr();
machStatePtr->savedMask &= ~(1 << i);
Mach_EnableIntr();
/*
* Push the window to the stack.
*/
if (Vm_CopyOut(MACH_SAVED_WINDOW_SIZE,
(Address)(machStatePtr->savedRegs[i]),
(Address)(machStatePtr->savedSps[i])) != SUCCESS) {
printf("MachUserAction: pid 0x%x being killed: %s 0x%x.\n",
procPtr->processID, "bad stack pointer?",
machStatePtr->savedSps[i]);
Proc_ExitInt(PROC_TERM_DESTROYED, PROC_BAD_STACK, 0);
}
}
}
}
/*
* We must check again here to see if the specialHandling flag has been
* set again. We've been taking interrupts this whole time, and the
* Vm code above may have called deeply, so we may have saved further
* user windows to internal buffers. If we have, go back up to the
* beginning of the routine, and do this all again.
*/
/*
* For now, we also flush all the windows to make sure the signal-handling
* stuff below won't cause us to save windows to internal buffers. This
* is a slow thing to do, but it is currenlty unclear what to do if
* the signal handler causes a window to get saved to an internal buffer.
* It will have to call some sort of MachUserAction-type routine itself
* in that case.
*/
Mach_DisableIntr();
Mach_FlushWindowsToStack();
if (procPtr->specialHandling != 0) {
goto HandleItAgain;
}
Mach_EnableIntr();
/*
* Check for floating point problems.
*/
if (machStatePtr->fpuStatus & MACH_FPU_EXCEPTION_PENDING) {
HandleFPUException(procPtr, machStatePtr);
}
/*
* Now check for signal stuff. We must check again for floating
* point exception because the Sig_Handle might do a context switch
* during which the excpetion would get posted.
*
* Note: This is really wrong. We should check for and process
* any floating point exceptions before handling a signal.
* The problem here is by the time Sig_Handle returns we are
* already committed to doing this signal.
*/
sigStackPtr = &(machStatePtr->sigStack);
sigStackPtr->contextPtr = &(machStatePtr->sigContext);
if (procPtr->unixProgress == PROC_PROGRESS_RESTART ||
procPtr->unixProgress > 0) {
/*
* If we received a normal signal, we want to restart
* the system call when we leave.
* If we received a migrate signal, we will get here on
* the new machine.
* We must also ensure that the argument registers are the
* same as when we came in.
*/
restarted = 1;
if (debugProcStubs) {
printf("Restarting system call with progress %d\n",
procPtr->unixProgress);
}
procPtr->unixProgress = PROC_PROGRESS_UNIX;
}
if (Sig_Handle(procPtr, sigStackPtr, &pc)) {
machStatePtr->sigContext.machContext.pcValue = pc;
machStatePtr->sigContext.machContext.trapInst = MACH_SIG_TRAP_INSTR;
/* leave interrupts disabled */
if (machStatePtr->fpuStatus & MACH_FPU_EXCEPTION_PENDING) {
HandleFPUException(procPtr, machStatePtr);
}
Mach_DisableIntr();
if (procPtr->unixProgress != PROC_PROGRESS_NOT_UNIX) {
/*
* We have to build a proper Unix signal stack.
*/
int n[16];
struct sigcontext unixContext;
procPtr->unixProgress = PROC_PROGRESS_UNIX;
if (Compat_SpriteSignalToUnix(sigStackPtr->sigNum,
&unixSignal) != SUCCESS) {
printf("Signal %d invalid in SetupSigHandler\n",
sigStackPtr->sigNum);
return 0;
}
if (restarted) {
if (debugProcStubs) {
printf("Moving PC to restart system call (doing signal\n");
}
machStatePtr->trapRegs->nextPc = machStatePtr->trapRegs->pc;
machStatePtr->trapRegs->pc = machStatePtr->trapRegs->nextPc-4;
/*
* We need to restore %o0 which got clobbered by
* the system call.
*/
machStatePtr->trapRegs->ins[0] = machStatePtr->savedArgI0;
}
if (debugProcStubs) {
printf("Unix signal %d(%d) to %x\n", sigStackPtr->sigNum,
unixSignal, procPtr->processID);
}
sigStackPtr->sigNum = unixSignal;
unixContext.sc_onstack = 0;
unixContext.sc_mask = machStatePtr->sigContext.oldHoldMask;
unixContext.sc_sp = machStatePtr->trapRegs->ins[6];
/*
* pc and npc are where to continue the interrupted routine.
*/
if (debugProcStubs) {
printf("PSR = %x\n", machStatePtr->trapRegs->curPsr);
}
unixContext.sc_pc = machStatePtr->trapRegs->pc;
unixContext.sc_npc = machStatePtr->trapRegs->nextPc;
if (debugProcStubs) {
printf("trapRegs->pc=%x, trapRegs->npc=%x, context.pcValue=%x\n",
machStatePtr->trapRegs->pc, machStatePtr->trapRegs->nextPc,
machStatePtr->sigContext.machContext.pcValue);
}
unixContext.sc_psr = machStatePtr->trapRegs->curPsr;
unixContext.sc_g1 = machStatePtr->trapRegs->globals[1];
unixContext.sc_o0 = machStatePtr->trapRegs->ins[0];
/*
* machContext.pcValue is the address of the handler.
*/
machStatePtr->trapRegs->pc = (unsigned int)
machStatePtr->sigContext. machContext.pcValue;
machStatePtr->trapRegs->nextPc = (unsigned int)
machStatePtr->sigContext.machContext.pcValue+4;
if (debugProcStubs) {
printf("new pc = %x\n", machStatePtr->trapRegs->nextPc);
}
/*
* Copy the window to the signal stack.
*/
Vm_CopyIn(16*sizeof(int), (Address)unixContext.sc_sp, (Address)n);
if (debugProcStubs) {
printf("Regs: %x %x %x, %x %x %x\n", n[0], n[1], n[2], n[8],
n[9], n[10]);
}
machStatePtr->trapRegs->ins[6] += MACH_SAVED_WINDOW_SIZE;
unixContext.sc_wbcnt = 0;
sigStackPtr->contextPtr = (Sig_Context *)
(unixContext.sc_sp-sizeof(struct sigcontext));
if (Vm_CopyOut(MACH_SAVED_WINDOW_SIZE,
(Address)n,
(Address)unixContext.sc_sp - sizeof(struct sigcontext)
- 4*sizeof(int) - MACH_SAVED_WINDOW_SIZE) !=
SUCCESS) {
return 0;
}
if (debugProcStubs) {
printf("Copied window to %x\n",
(Address)unixContext.sc_sp - sizeof(struct sigcontext)
- 4*sizeof(int) - MACH_SAVED_WINDOW_SIZE);
}
/*
* Copy the sigStack and sigContext to the signal window.
*/
if (Vm_CopyOut(4*sizeof(int), (Address)sigStackPtr,
(Address)unixContext.sc_sp - sizeof(struct sigcontext)
- 4*sizeof(int)) != SUCCESS) {
return 0;
}
if (Vm_CopyOut(sizeof(struct sigcontext), (Address)&unixContext,
(Address)unixContext.sc_sp - sizeof(struct sigcontext))
!= SUCCESS) {
return 0;
}
return 2;
} else {
return 1;
}
} else {
if (procPtr->unixProgress == PROC_PROGRESS_MIG_RESTART ||
procPtr->unixProgress == PROC_PROGRESS_RESTART) {
restarted = 1;
if (debugProcStubs) {
printf("No signal action, so we restarted call\n");
}
procPtr->unixProgress = PROC_PROGRESS_UNIX;
} else if (restarted && debugProcStubs) {
printf("No signal, yet we restarted system call!\n");
}
}
if (machStatePtr->fpuStatus & MACH_FPU_EXCEPTION_PENDING) {
HandleFPUException(procPtr, machStatePtr);
}
/*
* It is possible for Sig_Handle to mask the migration signal
* if a process is not in a state where it can be migrated.
* As soon as we return to user mode, though, we will allow migration.
* Clear the bit anytime something's pending, for simplicity.
*/
if (procPtr->sigPendingMask) {
Sig_AllowMigration(procPtr);
}
if (restarted) {
if (debugProcStubs) {
printf("Moving PC to restart system call (no signal\n");
}
machStatePtr->trapRegs->nextPc = machStatePtr->trapRegs->pc;
machStatePtr->trapRegs->pc = machStatePtr->trapRegs->nextPc-4;
/*
* We need to restore %o0 which got clobbered by
* the system call.
*/
machStatePtr->trapRegs->ins[0] = machStatePtr->savedArgI0;
procPtr->unixProgress = PROC_PROGRESS_UNIX;
}
if (procPtr->unixProgress != PROC_PROGRESS_NOT_UNIX &&
procPtr->unixProgress != PROC_PROGRESS_UNIX && debugProcStubs) {
printf("UnixProgress = %d leaving MachUserReturn\n",
procPtr->unixProgress);
}
/*
* Go back to MachReturnFromTrap. We are expected to have interrupts
* off there.
*/
Mach_DisableIntr();
return 0;
}
/*
*----------------------------------------------------------------------
*
* MachHandleTrap --
*
* Handle an instruction trap, such as an illegal instruction trap,
* an unaligned address, or a floating point problem.
*
* Results:
* None.
*
* Side effects:
* If it occured in the kernel, we panic. If it occured in a user
* program, we take appropriate signal action.
*
*----------------------------------------------------------------------
*/
void
MachHandleTrap(trapType, pcValue, trapPsr)
int trapType;
Address pcValue;
unsigned int trapPsr;
{
Proc_ControlBlock *procPtr;
/*
* Find the current process. If we took a MACH_FP_EXCEP at one of the
* marked FPU sync instructions, then we use the process saved
* in machFPUSaveProcPtr.
*/
procPtr = ((trapType == MACH_FP_EXCEP) &&
(pcValue == (Address) machFPUSyncInst)) ?
machFPUSaveProcPtr : Proc_GetCurrentProc();
if ((procPtr == (Proc_ControlBlock *) NIL)) {
printf("%s: pc = 0x%x, trapType = %d\n",
"MachHandleTrap", pcValue, trapType);
panic("Current process was NIL!\n");
}
/*
* Handle kernel-mode traps.
*/
if (trapPsr & MACH_PS_BIT) {
switch (trapType) {
case MACH_ILLEGAL_INSTR:
printf("%s %s\n", "MachHandleTrap: illegal",
"instruction trap in the kernel!");
break;
case MACH_PRIV_INSTR:
printf("%s %s\n", "MachHandleTrap: privileged",
"instruction trap in the kernel!");
break;
case MACH_MEM_ADDR_ALIGN:
printf("%s %s\n", "MachHandleTrap: unaligned",
"address trap in the kernel!");
break;
case MACH_TAG_OVERFLOW:
printf("%s %s\n", "MachHandleTrap: tag",
"overflow trap in the kernel!");
break;
case MACH_FP_EXCEP: {
#ifndef NO_FLOATING_POINT
/*
* We got a FP execption while running in kernel mode. If this
* exception occured at a known location we clear the
* exception and mark the Mach_State.
*/
if (pcValue == (Address) machFPUDumpSyncInst) {
/*
* Already doing a MachFPUDumpState. Whoever's doing the
* dump should check the pending flag and set fpuStatus if
* it's set.
*/
procPtr->machStatePtr->fpuStatus |=
MACH_FPU_EXCEPTION_PENDING;
return;
}
if (pcValue == (Address) machFPUSyncInst) {
MachFPUDumpState(procPtr->machStatePtr->trapRegs);
procPtr->machStatePtr->fpuStatus |=
(procPtr->machStatePtr->trapRegs->fsr
& MACH_FSR_TRAP_TYPE_MASK) |
MACH_FPU_EXCEPTION_PENDING;
procPtr->specialHandling = 1;
return;
}
printf("%s. ",
"MachHandleTrap: FPU exception from kernel process.");
break;
#else /* NO_FLOATING_POINT */
printf("Floating point op in kernel code: not supported in this\n");
panic("kernel due to copyright reasons.");
#endif /* NO_FLOATING_POINT */
}
case MACH_FP_DISABLED:
printf("%s %s\n", "MachHandleTrap: fp unit",
"disabled trap in the kernel!");
break;
default:
printf("%s %s\n", "MachHandleTrap: hit default",
"in case statement - bad trap instruction called us!");
break;
}
panic("%s %s %s %x %s %x\n",
"MachHandleTrap: the error occured in a",
procPtr->genFlags & PROC_USER ? "user" : "kernel",
"process, with procPtr =", (unsigned int) procPtr,
"and pc =", pcValue);
}
/*
* The trap occured in user-mode.
*/
switch (trapType) {
case MACH_ILLEGAL_INSTR:
(void) Sig_Send(SIG_ILL_INST, SIG_ILL_INST_CODE, procPtr->processID,
FALSE, pcValue);
break;
case MACH_PRIV_INSTR:
(void) Sig_Send(SIG_ILL_INST, SIG_PRIV_INST, procPtr->processID,
FALSE, pcValue);
break;
case MACH_MEM_ADDR_ALIGN:
(void) Sig_Send(SIG_ADDR_FAULT, SIG_ADDR_ERROR, procPtr->processID,
FALSE, (Address)0);
break;
case MACH_FP_EXCEP: {
unsigned int fsr;
/*
* An FP exception from user mode. Clear the exception and
* mark it in the Mach_State struct.
*
*/
MachFPUDumpState(procPtr->machStatePtr->trapRegs);
fsr = procPtr->machStatePtr->trapRegs->fsr;
if (!(procPtr->machStatePtr->fpuStatus & MACH_FPU_ACTIVE)) {
printf(
"FPU exception from process without MACH_FPU_ACTIVE, fsr = 0x%x\n",fsr);
}
procPtr->machStatePtr->fpuStatus |= (fsr & MACH_FSR_TRAP_TYPE_MASK) |
MACH_FPU_EXCEPTION_PENDING;
procPtr->specialHandling = 1;
break;
}
case MACH_FP_DISABLED: {
register Mach_State *machStatePtr;
Mach_FlushWindowsToStack();
machStatePtr = procPtr->machStatePtr;
/*
* Upon a user's first FPU disable trap we initialize and enable
* the FPU for him.
*/
if (machStatePtr->fpuStatus & MACH_FPU_ACTIVE) {
panic("Double FPU_DISABLE trap.\n");
}
machStatePtr->fpuStatus = MACH_FPU_ACTIVE;
/*
* Enable the FPU in the trap PSR.
*/
machStatePtr->trapRegs->curPsr |= MACH_ENABLE_FPP;
/*
* Initialize the FPU registers.
*/
machStatePtr->trapRegs->fsr = 0;
bzero((Address) (machStatePtr->trapRegs->fregs), MACH_NUM_FPS*4);
MachFPULoadState(machStatePtr->trapRegs);
break;
}
case MACH_TAG_OVERFLOW:
panic("%s %s\n", "MachHandleTrap: tag",
"overflow trap in user process, but I don't deal with it yet.");
break;
default:
panic("%s %s\n", "MachHandleTrap: hit default",
"in case statement - bad trap instruction from user mode.");
break;
}
return;
}
/*
*----------------------------------------------------------------------
*
* FlushTheWindows --
*
* A recursive C routine that will force window overflows and thereby
* flush the register windows to the stack.
*
* Results:
* None.
*
* Side effects:
* The register windows are flushed.
*
*----------------------------------------------------------------------
*/
static void
FlushTheWindows(num)
int num;
{
num--;
if (num > 0) {
FlushTheWindows(num);
}
return;
}
/*
*----------------------------------------------------------------------
*
* Mach_FlushWindowsToStack --
*
* Calls a routine to flush the register windows to the stack.
* This routine can be caled from traps, or wherever.
*
* Results:
* None.
*
* Side effects:
* The register windows are flushed.
*
*----------------------------------------------------------------------
*/
void
Mach_FlushWindowsToStack()
{
/*
* We want to do NWINDOWS - 1 saves and then restores to make sure all our
* register windows have been saved to the stack. Calling here does one
* save, so we want to do NWINDOWS - 2 more calls and returns.
*/
FlushTheWindows(MACH_NUM_WINDOWS - 2);
return;
}
/*
*----------------------------------------------------------------------
*
* MachUserDebug --
*
* This will cause the current process to go into the debugger. It can
* be called from trap handlers, etc. It first checks to see if the
* current process is NIL.
*
* Results:
* None.
*
* Side effects:
* The process gets a breakpoint signal.
*
*----------------------------------------------------------------------
*/
void
MachUserDebug()
{
Proc_ControlBlock *procPtr;
procPtr = Proc_GetCurrentProc();
if (procPtr == (Proc_ControlBlock *) NIL) {
panic("MachUserDebug: current process was NIL!\n");
}
Sig_Send(SIG_BREAKPOINT, SIG_NO_CODE, procPtr->processID, FALSE,
(Address)0);
return;
}
/*
*----------------------------------------------------------------------
*
* Mach_GetBootArgs --
*
* Returns the arguments out of the boot parameter structure.
*
* Results:
* Number of elements returned in argv.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Mach_GetBootArgs(argc, bufferSize, argv, buffer)
int argc; /* Number of elements in argv */
int bufferSize; /* Size of buffer */
char **argv; /* Ptr to array of arg pointers */
char *buffer; /* Storage for arguments */
{
#ifdef sun4c
if (romVectorPtr->v_romvec_version < 2) {
#endif
int i;
int offset;
bcopy(machMonBootParam.strings, buffer,
(bufferSize < 100) ? bufferSize : 100);
offset = (unsigned int) machMonBootParam.strings -(unsigned int) buffer;
for(i = 0; i < argc; i++) {
if (machMonBootParam.argPtr[i] == (char *) 0 ||
machMonBootParam.argPtr[i] == (char *) NIL) {
break;
}
argv[i] = (char *) (machMonBootParam.argPtr[i] - (char *) offset);
}
return i;
#ifdef sun4c
} else {
char *bufEndPtr = buffer + bufferSize - 1;
char *bufferPtr = buffer;
int argcsLeft = argc;
char *p;
/*
* On version 2 and greater the bootstring is stored in
* two null terminated strings. We copy these strings
* into the argc,argv format needed by Mach_GetBootArgs.
*/
if (argc == 0) {
return 0;
}
p = *(romVectorPtr->bootpath);
while (*p && isspace(*p)) { /* Skip any spaces. */
p++;
}
*argv = bufferPtr;
argv++; argcsLeft--;
while (*p && (bufferPtr < bufEndPtr)) {
*bufferPtr++ = *p++;
}
*bufferPtr++ = 0;
p = *(romVectorPtr->bootargs);
while (*p && isspace(*p)) { /* Skip any spaces. */
p++;
}
while ((bufferPtr < bufEndPtr) && (argcsLeft > 0) && *p) {
*argv = bufferPtr;
argv++; argcsLeft--;
while (*p && !isspace(*p) && (bufferPtr < bufEndPtr)) {
*bufferPtr++ = *p++;
}
*bufferPtr++ = 0;
while (*p && isspace(*p)) { /* Skip any spaces. */
p++;
}
}
return argc - argcsLeft;
}
#endif /* sun4c */
}
/*
*----------------------------------------------------------------------
*
* Mach_GetStackPointer --
*
* This is a stub routine for the sun4.
*
* Results:
* Address.
*
* Side effects:
* It panics since it should never be called. If it ends up being
* useful someday, change it so it doesn't panic.
*
*----------------------------------------------------------------------
*/
Address
Mach_GetStackPointer()
{
panic("Mach_GetStackPointer");
return NULL;
}
/*
*----------------------------------------------------------------------
*
* HandleFPUException --
*
* Handle any FPU exception present.
*
* Results:
* None.
*
* Side effects:
* FPU instruction emulated, process may be sent signal.
*
*----------------------------------------------------------------------
*/
static void
HandleFPUException(procPtr, machStatePtr)
Proc_ControlBlock *procPtr; /* Process control block of offending process*/
Mach_State *machStatePtr; /* Machine state of process. */
{
int i;
Mach_RegWindow *curWindow;
switch ((int) (machStatePtr->fpuStatus & MACH_FSR_TRAP_TYPE_MASK)) {
case MACH_FSR_IEEE_TRAP:
case MACH_FSR_UNFINISH_TRAP:
case MACH_FSR_UNIMPLEMENT_TRAP:
break;
case MACH_FSR_SEQ_ERRROR_TRAP: {
panic("Floating point sequence error, fsr = 0x%x\n",
machStatePtr->trapRegs->fsr);
break;
}
case MACH_FSR_NO_TRAP:
default: {
panic("Floating point exception with bad trap code, fsr = 0x%x\n",
machStatePtr->trapRegs->fsr);
break;
}
}
machStatePtr->fpuStatus &=
~(MACH_FPU_EXCEPTION_PENDING|MACH_FSR_TRAP_TYPE_MASK);
/*
* Emulate the evil instructions, and restore the result into the FPU.
*/
curWindow = (Mach_RegWindow *)
(machStatePtr->trapRegs->ins[MACH_FP_REG]);
for (i = 0; i < machStatePtr->trapRegs->numQueueEntries; i++) {
#ifndef NO_FLOATING_POINT
MachFPU_Emulate(procPtr->processID,
machStatePtr->trapRegs->fqueue[i].address,
machStatePtr->trapRegs,
curWindow);
#else /* NO_FLOATING_POINT */
printf(
"Cannot emulate floating point operations in this kernel version\n");
printf("Killing process 0x%x\n", procPtr->processID);
(void) Sig_Send(SIG_ILL_INST, SIG_ILL_INST_CODE, procPtr->processID,
FALSE, machStatePtr->trapRegs->pc);
#endif /* NO_FLOATING_POINT */
}
MachFPULoadState(machStatePtr->trapRegs);
}
/*
*----------------------------------------------------------------------
*
* Mach_SigreturnStub --
*
* Return from a unix signal or long jump.
*
* Results:
* None.
*
* Side effects:
* Changes control of execution.
*
*----------------------------------------------------------------------
*/
int
Mach_SigreturnStub(jmpBuf)
jmp_buf *jmpBuf;
{
struct sigcontext context;
Proc_ControlBlock *procPtr = Proc_GetCurrentProc();
Mach_State *machStatePtr = procPtr->machStatePtr;
if (Vm_CopyIn(9*sizeof(int), (Address)jmpBuf, (Address)&context) !=
SUCCESS) {
printf("jmp_buf copy in failure\n");
return -1;
}
if (debugProcStubs) {
printf("Unix sigreturn: pc = %x, sp = %x, psr = %x\n", context.sc_pc,
context.sc_sp, context.sc_psr&MACH_DISABLE_TRAP_BIT &
~MACH_PS_BIT);
}
/*
* Flush register windows to stack and maybe our problems will go away.
*/
Mach_DisableIntr();
Mach_FlushWindowsToStack();
Mach_EnableIntr();
machStatePtr->trapRegs->pc = context.sc_pc;
machStatePtr->trapRegs->nextPc = context.sc_npc;
machStatePtr->trapRegs->globals[1] = context.sc_g1;
machStatePtr->trapRegs->ins[0] = context.sc_o0;
machStatePtr->sigContext.oldHoldMask = context.sc_mask;
machStatePtr->trapRegs->curPsr = (machStatePtr->trapRegs->curPsr&
~MACH_PSR_SIG_RESTORE) | (context.sc_psr&MACH_PSR_SIG_RESTORE);
machStatePtr->trapRegs->ins[6] = context.sc_sp;
Sig_Return(procPtr, &machStatePtr->sigStack);
return 0; /* Dummy */
}
/*
*----------------------------------------------------------------------
*
* Mach_FastBoot --
*
* Do a fast reboot (using copied initialized heap data, etc.)
*
* Results:
* FAILURE if we're not set up to do a fast boot. Otherwise, we don't
* return, but boot instead.
*
* Side effects:
* Will probably cause fast reboot.
*
*----------------------------------------------------------------------
*/
ReturnStatus
Mach_FastBoot()
{
if (!recov_DoInitDataCopy) {
printf("Can fast reboot: initialized data wasn't copied.");
return FAILURE;
}
MachDoFastBoot();
return FAILURE; /* Should never get here. */
}
/*
*----------------------------------------------------------------------
*
* Mach_GetMachineType --
*
* Get the machine type from the idprom.
*
* Results:
* The machine type.
*
* Side effects:
* Reads from PROM the first time.
*
*----------------------------------------------------------------------
*/
#ifdef sun4c
int
Mach_GetMachineType()
{
if (machIdProm.id_format != IDFORM_1) {
if (Mach_MonSearchProm("*", "idprom", (char *)&machIdProm,
sizeof machIdProm) != sizeof machIdProm) {
panic("Where is the idprom?");
}
}
return machIdProm.id_machine;
}
#endif /* sun4c */
/*
*----------------------------------------------------------------------
*
* Mach_GetEtherAddress --
*
* Get the ethernet address.
*
* Results:
* A pointer to the ethernet address.
*
* Side effects:
* Reads from PROM the first time.
*
*----------------------------------------------------------------------
*/
#ifdef sun4c
Net_EtherAddress *
Mach_GetEtherAddress(etherAddressPtr)
Net_EtherAddress *etherAddressPtr;
{
if (machIdProm.id_format != IDFORM_1) {
if (Mach_MonSearchProm("*", "idprom", (char *)&machIdProm,
sizeof machIdProm) != sizeof machIdProm) {
panic("Where is the idprom?");
}
}
bcopy((char *)machIdProm.id_ether, (char *)etherAddressPtr,
sizeof(Net_EtherAddress));
return etherAddressPtr; /* which copy should I return? */
}
#endif /* sun4c */